#include <buola/gl/ctexture.h>
#include <buola/image/canyview.h>
#include <buola/image/cgradient.h>

namespace buola { namespace gl {

void CTexture::DoCreate1DAndBind()
{
    if(!mID) glGenTextures(1,&mID);
    mTarget=GL_TEXTURE_1D;
    Bind();
    glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
}

void CTexture::DoCreate2DAndBind()
{
    if(!mID) glGenTextures(1,&mID);
    mTarget=GL_TEXTURE_2D;
    Bind();
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
}

void CTexture::Create1D(const img::CGradient &pGradient)
{
    DoCreate1DAndBind();
    img::CImage_rgba8 lImage({1024,1});

    const auto &lStops=pGradient.Stops();
    if(lStops.empty()) throw XInvalid("creating texture from empty gradient");

    int s=0;
    int j=0;

    while(s+1<lStops.size()&&lStops[s+1].second<=0.0) s++;

    int lEnd=std::min(1023,(int)(lStops[s].second*1023.0));
    for(j=0;j<lEnd;j++)
    {
        lImage(j,0)=img::CPixel_rgba8(lStops[s].first);
    }
        
    while(++s<lStops.size()&&lEnd<1024)
    {
        int lBegin=j-1;
        lEnd=std::min(1023,(int)(lStops[s].second*1023.0));
        double lStep=1.0/(lEnd-lBegin);
        for(;j<=lEnd;j++)
            lImage(j,0)=img::CPixel_rgba8(mix(lStops[s-1].first,lStops[s].first,(j-lBegin)*lStep));
    }

    for(;j<1024;j++)
    {
        lImage(j,0)=img::CPixel_rgba8(lStops[s-1].first);
    }

    glTexImage1D(GL_TEXTURE_1D,0,GL_RGBA,1024,0,GL_RGBA,GL_UNSIGNED_BYTE,lImage.RawBytes());
}

    
void CTexture::Create2D(const img::CAnyView &pSrc)
{
    DoCreate2DAndBind();
    
    GLint lInternalFormat;
    GLenum lFormat;
    GLenum lType;
    size_t lStride;
    
    switch(pSrc.Format())
    {
    case img::FORMAT_RGBA8:
        lInternalFormat=GL_RGBA;
        lFormat=GL_RGBA;
        lType=GL_UNSIGNED_BYTE;
        lStride=pSrc.Stride()/4;
        break;
    case img::FORMAT_BGRA8:
        lInternalFormat=GL_RGBA;
        lFormat=GL_BGRA;
        lType=GL_UNSIGNED_BYTE;
        lStride=pSrc.Stride()/4;
        break;
    case img::FORMAT_RGB8:
        lInternalFormat=GL_RGB;
        lFormat=GL_RGB;
        lType=GL_UNSIGNED_BYTE;
        lStride=pSrc.Stride()/3;
        break;
    case img::FORMAT_GRAY8:
        lInternalFormat=GL_RED;
        lFormat=GL_RED;
        lType=GL_UNSIGNED_BYTE;
        lStride=pSrc.Stride();
        break;
    default:
        throw XInvalid("unsupported format in CTexture::Create2D");
    }
    
    glPixelStorei(GL_UNPACK_ROW_LENGTH,lStride);
    glTexImage2D(GL_TEXTURE_2D,0,lInternalFormat,pSrc.Width(),pSrc.Height(),0,lFormat,lType,pSrc.RawBytes());
}

/*namespace gl*/ } /*namespace buola*/ }
